package taskit.test;
import java.util.*;
import java.io.*;
import java.text.*;

/**
 * class Log is used for logging messages which can be used to detect
 * input and output and verify their integrity
 * @author Joseph Matthias Goh
 *
 */
// @author A0087072A
public class Log implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public static final String FILENAME_LOG = "taskit.log";
	private static ArrayList<String> messages = new ArrayList<String>();
	private static java.util.Date timeNow;
	private static DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss: ");
	
	/**
	 * <region: test functions>
	 */
	private static final String [] defaultTestMessages = {
			"hello, ", "bye, ", "shao ning", "yusuf", "shiwani ", "  joseph", "",
			"\tlo\tl\nhi hi", "\nlol\n", "just a sentence     to try out   white  spaces",
			"w\t\ni\nl\nl\tthi\ns\t\tfa\ni\nl?", "~!@#$%^&*()_+QWERTYUIOP{}}|ASDFGHJKL:\"ZXCVBNM<>?}",
			"\n\nbegin with new line, end with white space!  " };
	public static boolean test() {
		Random number = new Random();
		int numberOfTrials = number.nextInt(100);
		int numberOfMessagesPerTrial = number.nextInt(100);
		boolean passed;
		long startTime = System.nanoTime();
		passed = testDriver(numberOfTrials, numberOfMessagesPerTrial);
		long endTime = System.nanoTime();
		long duration = endTime - startTime;
		String output = (passed ? "Test passed " : "Test failed ") +
				"for " + numberOfTrials +
				" trials, each containing " + numberOfMessagesPerTrial +
				" random entries in " + duration/1000000 + " ms";
		System.out.println(output);
		return passed;
	}
	public static boolean testDriver(int combineHowMany, int doHowManyTimes) {
		return testDriver(combineHowMany, doHowManyTimes, defaultTestMessages);
	}
	public static boolean testDriver(int combineHowMany, int doHowManyTimes, String[] testMessages) {
		int testMessageSize = testMessages.length;
		String expected = "";
		String output = "";
		Random rand = new Random();
		int randomNumber;
		boolean testPassed = true;
		for(int times = 0; times < doHowManyTimes; ++ times) {
			expected = "";
			for(int counter = 0; counter < combineHowMany; ++counter) {
				randomNumber = rand.nextInt(testMessageSize);
				Log.add(testMessages[randomNumber], false);
				expected += (testMessages[randomNumber].trim() + "\n");
			}
			output = Log.getAll(true);
			System.out.println("[TRIAL " + times + "]\n");
			System.out.println("Output\n^^^^^^\n" + output + "\n");
			System.out.println("Expected\n^^^^^^^^\n" + expected);
			if(output.compareTo(expected) != 0) {
				testPassed = false;
				break;
			} else {
				Log.clear();
			}
		}
		return testPassed;
	}
	/**
	 * </region: test functions>
	 */
	
	/**
	 * adds a message to the log including timestamp
	 * @param message :- the message to be added
	 */
	public static void add(String message) {
		add(message, true);
	}
	
	/**
	 * adds a message to the log, giving the option whether to include timestamp
	 * @param message :- the message to be added
	 * @param timestampVisible :- whether the timestamp will be visible
	 */
	public static void add(String message, boolean timestampVisible) {
		if(timestampVisible) {
			timeNow = new java.util.Date();
			message = dateFormat.format(timeNow) + message;
		}
		System.out.println(message.trim());
		messages.add(message.trim());
	}
	
	/**
	 * adds an array of messages to the log including timestamp
	 * @param messages :- messages to be added
	 */
	public static void add(String [] messages) {
		add(messages, true);
	}
	
	/**
	 * adds an array of messages to the log giving the option to include timestamp
	 * @param messages :- messages to be added
	 * @param timestampVisible :- whether the timestamp is visible
	 */
	public static void add(String [] messages, boolean timestampVisible) {
		int numberOfMessages = messages.length;
		for(int counter = 0; counter < numberOfMessages; ++counter) {
			add(messages[counter], timestampVisible);
		}
	}
	
	/**
	 * clears the log of any messages
	 */
	public static void clear() {
		messages.clear();
	}
	
	/**
	 * outputs everything that is in the log into taskit.log
	 */
	public static void dump() {
		File file;
		
		try {
			file = new File(FILENAME_LOG);
			if(!file.exists()) {
				try {
					file.createNewFile();
				} catch(IOException ioex) {
					return;
				}
			} 
		} catch(Exception ex) {
			return;
		}
		
		try {
			FileWriter fwriter = new FileWriter(file);
			fwriter.write(getAll());
			fwriter.close();
		} catch(IOException ioex) {
			return;
		}
	}
	
	/**
	 * returns the entire log, each entry separated from the next by a newline
	 * unless the entry itself contains newlines
	 * @return :- the entire log in System.String format
	 */
	public static String getAll() {
		return getAll(true);
	}
	
	/**
	 * returns the entire log in System.String format depending on
	 * whether <i>appendNewLine</i> is true
	 * @param appendNewline :- whether entries should be separated by a newline
	 * @return :- the entire log in System.String format
	 */
	public static String getAll(boolean appendNewline) {
		StringBuilder returnValue = new StringBuilder();
		int numberOfMessages = messages.size();
		for(int counter = 0; counter < numberOfMessages; ++counter) {
			returnValue.append(messages.get(counter) + (appendNewline ? "\n" : ""));
		}
		return returnValue.toString();
	}
	
	/**
	 * Serializable
	 */
	@Override
	public String toString() {
		return Log.getAll();
	}
	
	/**
	 * Calls the test function
	 * @param args :- command line arguments
	 */
	public static void main(String[] args) {
		test();
	}
}
